#include "st7305_1p54_driver.h"
#include "driver/spi_master.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_io_spi.h"
#include "esp_log.h"
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "FreeRTOSConfig.h"
#include <array>
#include "sdkconfig.h"

#define SPI_FREQ   CONFIG_YDP_TFT_SPI_FREQUENCY
//#define LCD_DATA_WIDTH 51

//ST7305 CPOL=1,CPHA=1,f<33.3MHz
#define ABS_DIFF(x, y) (((x) > (y))? ((x) - (y)) : ((y) - (x)))
static const char* TAG = "st7305_driver";

uint32_t frame_num = 0;
struct Command {
    uint8_t command;
    std::array<uint8_t, 10> data;  // 假设最大数据长度为10，根据实际需要调整
    size_t dataSize;
};

const std::array<Command, 28> Commands = {{
    {0xD6, {{0x17, 0x02}}, 2},
    {0xD1, {{0x01}}, 1},
    {0xC0, {{0x12, 0x0A}}, 2},
    {0xC1, {{115,0x3E,0x3C,0x3C}}, 4},
    {0xC2, {{0,0x21,0x23,0x23}}, 4},
    {0xC4, {{50,0x5C,0x5A,0x5A}}, 4},
    {0xC5, {{50,0x35,0x37,0x37}}, 4},
    {0xD8, {{0x80,0xE9}}, 2},
    {0xB2, {{0x12}}, 1},
    {0xB3, {{0xE5,0xF6,0x17,0x77,0x77,0x77,0x77,0x77,0x77,0x71}}, 10},
    {0xB4, {{0x05,0x46,0x77,0x77,0x77,0x77,0x76,0x45}}, 8},
    {0x62, {{0x32,0x03,0x1F}}, 3},
    {0xB7, {{0x13}}, 1},
    {0xB0, {{0x32}}, 1},
    {0x11, std::array<uint8_t, 10>{}, 0},
    {0xC9, {{0x00}}, 1},
    {0x36, {{0x48}}, 1},
    {0x3A, {{0x11}}, 1},
    {0xB9, {{0x20}}, 1},
    {0xB8, {{0x29}}, 1},
    {0x2A, {{0x16,0x26}}, 2},
    {0x2B, {{0x00,0x63}}, 2},
    {0x35, {{0x00}}, 1},
    {0xD0, {{0xFF}}, 1},
    {0x38, std::array<uint8_t, 10>{}, 0},
    {0x29, std::array<uint8_t, 10>{}, 0},
    {0x20, std::array<uint8_t, 10>{}, 0},
    {0xBB, {{0x4F}}, 1}
}};


ST7305_1p54_driver::ST7305_1p54_driver(gpio_num_t dcPin, gpio_num_t resPin, 
    gpio_num_t csPin, gpio_num_t sclkPin, gpio_num_t sdinPin) 
    : ST7305_UI(200, 200),
    DC_PIN(dcPin),
    RES_PIN(resPin),
    CS_PIN(csPin),
    SCLK_PIN(sclkPin),
    SDIN_PIN(sdinPin),
    LCD_WIDTH(200),
    LCD_HIGH(200),
    LCD_DATA_WIDTH(51),  // 200 pixels / 4 pixels per byte ≈ 51 bytes per line
    LCD_DATA_HIGH(100),  // 200 lines / 2 (shared lines) = 100 lines
    DISPLAY_BUFFER_LENGTH(LCD_DATA_WIDTH * LCD_DATA_HIGH)
{
    display_buffer = new uint8_t[DISPLAY_BUFFER_LENGTH];
    if (!display_buffer) {
        ESP_LOGE(TAG, "Failed to allocate display buffer");
    }
}

ST7305_1p54_driver::~ST7305_1p54_driver() {
    delete[] display_buffer;
}

void ST7305_1p54_driver::initialize() {
    Initial_ST7305();
    fill(0x00);
}

void ST7305_1p54_driver::fill(uint8_t data) {
    memset(display_buffer, data, DISPLAY_BUFFER_LENGTH);
}

void ST7305_1p54_driver::clearDisplay() {
    memset(display_buffer, 0x00, DISPLAY_BUFFER_LENGTH);
}

void ST7305_1p54_driver::writePoint(uint16_t x, uint16_t y, bool enabled) {
    if(x>=LCD_WIDTH || y>=LCD_HIGH){
        return;
    }
    else{
        x += 4;
        // 找到是哪一行的数据
        uint8_t real_x = x/4; // 0->0, 3->0, 4->1, 7->1
        uint8_t real_y = y/2; // 0->0, 1->0, 2->1, 3->1
        uint16_t write_byte_index = real_y*LCD_DATA_WIDTH+real_x;
        uint8_t one_two = (y % 2 == 0)?0:1; // 0 1
        uint8_t line_bit_4 = x % 4; // 0 1 2 3
        uint8_t write_bit = 7-(line_bit_4*2+one_two);

        if (enabled) {
            // 将指定位置的 bit 置为 1
            display_buffer[write_byte_index] |= (1 << write_bit);
        } else {
            // 将指定位置的 bit 置为 0
            display_buffer[write_byte_index] &= ~(1 << write_bit);
        }
    }
}

void ST7305_1p54_driver::writePoint(uint16_t x, uint16_t y, uint8_t data) {
    if(x>=LCD_WIDTH || y>=LCD_HIGH){
        return;
    }
    else{
        x += 4;
        // 找到是哪一行的数据
        uint8_t real_x = x/4; // 0->0, 3->0, 4->1, 7->1
        uint8_t real_y = y/2; // 0->0, 1->0, 2->1, 3->1
        uint16_t write_byte_index = real_y*LCD_DATA_WIDTH+real_x;
        uint8_t one_two = (y % 2 == 0)?0:1; // 0 1
        uint8_t line_bit_4 = x % 4; // 0 1 2 3
        uint8_t write_bit = 7-(line_bit_4*2+one_two);

        if (data != 0) {
            // 将指定位置的 bit 置为 1
            display_buffer[write_byte_index] |= (1 << write_bit);
        } else {
            // 将指定位置的 bit 置为 0
            display_buffer[write_byte_index] &= ~(1 << write_bit);
        }
    }
}

void ST7305_1p54_driver::display() {
    frame_update_done_flag = false;
    esp_lcd_panel_io_tx_param(lcd_io_handle, Commands[20].command, Commands[20].data.data(), Commands[20].dataSize);
    esp_lcd_panel_io_tx_param(lcd_io_handle, Commands[21].command, Commands[21].data.data(), Commands[21].dataSize);
    esp_lcd_panel_io_tx_color(lcd_io_handle, 0x2C, display_buffer,5100);
}

void ST7305_1p54_driver::Initial_ST7305() {
    spi_bus_config_t buscfg = {
        .mosi_io_num = SDIN_PIN,       //MOSI引脚
        .miso_io_num = -1,
        .sclk_io_num = SCLK_PIN,        //SCLK引脚
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
        .max_transfer_sz = DISPLAY_BUFFER_LENGTH,        //最大传输字节数
        .flags = SPICOMMON_BUSFLAG_MASTER , //SPI主模式
    };
    ESP_ERROR_CHECK(spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO));
    if(RES_PIN > 0)
    {
        gpio_config_t rst_gpio_cfg = 
        {
            .pin_bit_mask = (uint32_t)(1<<RES_PIN),                //GPIO脚
            .mode = GPIO_MODE_OUTPUT,                   //输出模式
            .pull_up_en = GPIO_PULLUP_DISABLE,          //禁止上拉
            .pull_down_en = GPIO_PULLDOWN_DISABLE,      //禁止下拉
            .intr_type = GPIO_INTR_DISABLE,             //禁止中断
        };
        gpio_config(&rst_gpio_cfg);
    }
    esp_lcd_panel_io_spi_config_t io_config = {
        .cs_gpio_num = CS_PIN,         //CS引脚
        .dc_gpio_num = DC_PIN,         //DC引脚
        .spi_mode = 3,                  //使用SPI3模式
        .pclk_hz = SPI_FREQ*1000*1000,        //SPI时钟频率
        .trans_queue_depth = 10,        //表示可以缓存的spi传输事务队列深度
        .on_color_trans_done = frame_update_done, //传输完成回调函数
        .user_ctx = this,
        .lcd_cmd_bits = 8,              //命令长度
        .lcd_param_bits = 8,            //参数长度
    };
    ESP_LOGI(TAG,"create esp_lcd_new_panel");
    ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)SPI2_HOST, &io_config, &lcd_io_handle));

    if(RES_PIN > 0)
    {
        gpio_set_level(RES_PIN,1);
        vTaskDelay(pdMS_TO_TICKS(10));
        gpio_set_level(RES_PIN,0);
        vTaskDelay(pdMS_TO_TICKS(10));
        gpio_set_level(RES_PIN,1);
        vTaskDelay(pdMS_TO_TICKS(10));
    }
    for (size_t i = 0; i <= 14; ++i) {
        const auto& cmd = Commands[i];
        esp_lcd_panel_io_tx_param(lcd_io_handle, cmd.command, cmd.data.data(), cmd.dataSize);
    }
    vTaskDelay(pdMS_TO_TICKS(120));
    for (size_t i = 15; i <= 27; ++i) {
        const auto& cmd = Commands[i];
        esp_lcd_panel_io_tx_param(lcd_io_handle, cmd.command, cmd.data.data(), cmd.dataSize);
    }

    HPM_MODE = true;
    LPM_MODE = false;

}

void ST7305_1p54_driver::Low_Power_Mode(){
    if(LPM_MODE){
        HPM_MODE = false;
        LPM_MODE = true;
    }else{
        HPM_MODE = false;
        LPM_MODE = true;
        for (size_t i = 3; i <= 6; ++i) {
            const auto& cmd = Commands[i];
            esp_lcd_panel_io_tx_param(lcd_io_handle, cmd.command, cmd.data.data(), cmd.dataSize);
        }
        esp_lcd_panel_io_tx_param(lcd_io_handle, Commands[15].command, Commands[15].data.data(), Commands[15].dataSize);
        vTaskDelay(pdMS_TO_TICKS(20));
        esp_lcd_panel_io_tx_param(lcd_io_handle,0x39,NULL,0);//LPM:Low Power Mode ON
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

void ST7305_1p54_driver::High_Power_Mode(){
    if(HPM_MODE){
        HPM_MODE = true;
        LPM_MODE = false;
    }else{
        HPM_MODE = true;
        LPM_MODE = false;
        esp_lcd_panel_io_tx_param(lcd_io_handle,0x38,NULL,0);//HPM:high Power Mode ON
        vTaskDelay(pdMS_TO_TICKS(300));
        for (size_t i = 3; i <= 6; ++i) {
            const auto& cmd = Commands[i];
            esp_lcd_panel_io_tx_param(lcd_io_handle, cmd.command, cmd.data.data(), cmd.dataSize);
        }
        esp_lcd_panel_io_tx_param(lcd_io_handle, Commands[15].command, Commands[15].data.data(), Commands[15].dataSize);
        vTaskDelay(pdMS_TO_TICKS(20));
    }
}

void ST7305_1p54_driver::display_on(bool enabled){
    if(enabled){
        esp_lcd_panel_io_tx_param(lcd_io_handle,0x29,NULL,0);//DISPLAY ON  
    }else{
        esp_lcd_panel_io_tx_param(lcd_io_handle,0x28,NULL,0);//DISPLAY OFF 
    }
}

void ST7305_1p54_driver::display_sleep(bool enabled){
    if(enabled){
        if(LPM_MODE){
            esp_lcd_panel_io_tx_param(lcd_io_handle,0x38,NULL,0);//HPM:high Power Mode ON
            vTaskDelay(pdMS_TO_TICKS(300));
        }
        esp_lcd_panel_io_tx_param(lcd_io_handle,0x10,NULL,0); //sleep ON  
        vTaskDelay(pdMS_TO_TICKS(100));
    }else{
        esp_lcd_panel_io_tx_param(lcd_io_handle,0x11,NULL,0); //sleep OFF  
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

void ST7305_1p54_driver::display_Inversion(bool enabled){
    if(enabled){
        esp_lcd_panel_io_tx_param(lcd_io_handle,0x21,NULL,0); //Display Inversion On 
    }else{
        esp_lcd_panel_io_tx_param(lcd_io_handle,0x20,NULL,0); //Display Inversion Off 
    }
}

bool ST7305_1p54_driver::frame_update_done(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx){
    ST7305_1p54_driver* instance = static_cast<ST7305_1p54_driver*>(user_ctx);
    instance->nonStaticFrame_update_done();
    return true;
}

void ST7305_1p54_driver::nonStaticFrame_update_done() {
    frame_update_done_flag = true;
    frame_num++;
}
